﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using Assist;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.ExtensibleStorage;
using YArchitech.Revit;
using YJKArchUtils.Utils;
using YJKRevitBase.Math;
using YJKRevitKernel.ElementSearchUtility.LinkDocumentSuite;
using YJKRevitKernel.GeometryUtility;
using YJKRevitDimensionText.VerticalAutoDim.GeoModel;
using YJKRevitDimensionText.VerticalAutoDim.Group;
using YJKRevitParapet.Controller;

namespace YJKRevitDimensionText.VerticalAutoDim
{
	internal class Utility
	{
		[DllImport("User32.dll")]
		public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

		public static IList<BuiltInCategory> GetAllCategory()
		{
			return new List<BuiltInCategory>
			{
				(BuiltInCategory)(-2000011),
				(BuiltInCategory)(-2000126),
				(BuiltInCategory)(-2000014),
				(BuiltInCategory)(-2000023),
				(BuiltInCategory)(-2000240)
			};
		}

		public static IList<Element> GetDocElement(Autodesk.Revit.DB.Document doc, Autodesk.Revit.DB.View view, IList<BuiltInCategory> lstCategory)
		{
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(doc, view.Id);
			ElementMulticategoryFilter elementMulticategoryFilter = new ElementMulticategoryFilter(lstCategory);
			return filteredElementCollector.WherePasses(elementMulticategoryFilter).ToElements();
		}

		public static IList<Element> GetAllWallByCurAndLinkDoc(Autodesk.Revit.DB.Document doc)
		{
			List<BaseLinkDocumentResultBean> list = new FilteredElementCollectorLinkDocument(doc).OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType().ToList<BaseLinkDocumentResultBean>();
			List<Element> list2 = new List<Element>();
			foreach (BaseLinkDocumentResultBean baseLinkDocumentResultBean in list)
			{
				list2.Add(baseLinkDocumentResultBean.UseElement);
			}
			return list2;
		}

		public static BoundingBoxXYZ GetViewCropRegion(Autodesk.Revit.DB.Document doc)
		{
			Autodesk.Revit.DB.View activeView = doc.ActiveView;
			if (!activeView.CropBoxActive)
			{
				return null;
			}
			Element element = new FilteredElementCollector(doc, activeView.Id).OfCategory(BuiltInCategory.OST_Viewers).WhereElementIsNotElementType().FirstElement();
			if (element == null)
			{
				return null;
			}
			return element.get_BoundingBox(doc.ActiveView);
		}

		public static Plane CreatePlaneByView(Autodesk.Revit.DB.View view)
		{
			Transform transform = view.CropBox.Transform;
			return RevitVersionFuncs.CreatePlanByOriginAndBasis(transform.Origin, transform.BasisX, transform.BasisY);
		}

		public static Transform CreateTransformByPlane(Plane pl)
		{
			Transform identity = Transform.Identity;
			identity.Origin = pl.Origin;
			identity.BasisX = pl.XVec;
			identity.BasisY = pl.YVec;
			identity.BasisZ = pl.Normal;
			return identity.Inverse;
		}

		public static bool IsViewCropRegionValid(Autodesk.Revit.DB.View view)
		{
			return view.CropBoxActive;
		}

		public static IList<ViewPlan> GetAllValidViewPlan(Autodesk.Revit.DB.Document doc, Func<ViewPlan, bool> IsValidViewPlan = null)
		{
			List<ViewPlan> list = new List<ViewPlan>();
			IEnumerable<ViewPlan> enumerable = from Autodesk.Revit.DB.ViewPlan v in new FilteredElementCollector(doc).OfClass(typeof(ViewPlan))
			where v.GenLevel != null && !v.IsTemplate
			select v;
			foreach (Level level in Common.GetSortLevels(doc, double.MinValue, double.MaxValue))
			{
				foreach (ViewPlan viewPlan in enumerable)
				{
					if (viewPlan.GenLevel.Id == level.Id && (IsValidViewPlan == null || IsValidViewPlan(viewPlan)))
					{
						list.Add(viewPlan);
						break;
					}
				}
			}
			return list;
		}

		public static Outline GetPlanarGeoBoxRegion(IList<IPlanarGeo> lstGeo)
		{
			try
			{
				List<IPlanarGeo> list = (from e in lstGeo
				where e.Weight != 11
				select e).ToList<IPlanarGeo>();
				IPlanarGeo planarGeo = list.First<IPlanarGeo>();
				Outline outline = new Outline(planarGeo.Min3DPointWCS, planarGeo.Max3DPointWCS);
				foreach (IPlanarGeo planarGeo2 in list)
				{
					outline.AddPoint(planarGeo2.Max3DPointWCS);
					outline.AddPoint(planarGeo2.Min3DPointWCS);
				}
				return outline;
			}
			catch
			{
			}
			return null;
		}

		public static bool IsValidParapetWall(Element elem)
		{
			Wall wall = elem as Wall;
            return wall != null && (new Regex("女儿墙").IsMatch(wall.WallType.Name) || YJKRevitParapet.Controller.ExtensibleStorageManager.HasParapetExtensibleStorage(wall));
		}

		public static bool IsValidParapetWall(BaseLinkDocumentResultBean elem)
		{
			return Utility.IsValidParapetWall(elem.UseElement);
		}

		public static void GetWallElevations(Element wall, Plane pl, Transform TransformWcs2Ecs, ref double bottom, ref double top)
		{
			Parameter parameter = RevitElementExtension.GetParameter(wall, BuiltInParameter.WALL_TOP_IS_ATTACHED);
			if (RevitElementExtension.GetParameter(wall, BuiltInParameter.WALL_BOTTOM_IS_ATTACHED).AsInteger() != 1 && parameter.AsInteger() != 1)
			{
				BoundingBoxXYZ boundingBoxXYZ = wall.get_BoundingBox(wall.Document.ActiveView);
				bottom = boundingBoxXYZ.Min.Z;
				top = boundingBoxXYZ.Max.Z;
				return;
			}
			List<Solid> allSolid = GeometryUtilityMethod.GetAllSolid(wall.get_Geometry(new Options()), null);
			List<XYZ> list = new List<XYZ>();
			foreach (Solid solid in allSolid)
			{
				Face face = null;
				try
				{
					face = ExtrusionAnalyzer.Create(solid, pl, pl.Normal).GetExtrusionBase();
				}
				catch
				{
					foreach (object obj in solid.Edges)
					{
						Edge edge = (Edge)obj;
						XYZ endPoint = edge.AsCurve().GetEndPoint(0);
						XYZ endPoint2 = edge.AsCurve().GetEndPoint(1);
						list.Add(TransformWcs2Ecs.OfPoint(endPoint));
						list.Add(TransformWcs2Ecs.OfPoint(endPoint2));
					}
					continue;
				}
				foreach (object obj2 in face.EdgeLoops)
				{
					foreach (object obj3 in ((EdgeArray)obj2))
					{
						Edge edge2 = (Edge)obj3;
						XYZ endPoint3 = edge2.AsCurve().GetEndPoint(0);
						XYZ endPoint4 = edge2.AsCurve().GetEndPoint(1);
						list.Add(TransformWcs2Ecs.OfPoint(endPoint3));
						list.Add(TransformWcs2Ecs.OfPoint(endPoint4));
					}
				}
			}
			if (EnumerableExternsion.IsEmpty<XYZ>(list))
			{
				return;
			}
			List<XYZ> source = (from e in list
			orderby e.Y
			select e).ToList<XYZ>();
			XYZ xyz = TransformWcs2Ecs.Inverse.OfPoint(source.Last<XYZ>());
			top = xyz.Z;
			XYZ xyz2 = TransformWcs2Ecs.Inverse.OfPoint(source.First<XYZ>());
			bottom = xyz2.Z;
		}

		public static IList<Curve> CreateRectBoundaryCvsInEcs(XYZ ptMinEcs, XYZ ptMaxEcs)
		{
			List<Curve> list = new List<Curve>(4);
			double z = ptMinEcs.Z;
			XYZ xyz = new XYZ(ptMaxEcs.X, ptMinEcs.Y, z);
			XYZ xyz2 = new XYZ(ptMinEcs.X, ptMaxEcs.Y, z);
			list.Add(Line.CreateBound(ptMinEcs, xyz));
			list.Add(Line.CreateBound(xyz, ptMaxEcs));
			list.Add(Line.CreateBound(ptMaxEcs, xyz2));
			list.Add(Line.CreateBound(xyz2, ptMinEcs));
			return list;
		}

		public static ICollection<ElementId> GetVerticalDimElementByDocView(Autodesk.Revit.DB.Document doc)
		{
			ElementMulticategoryFilter elementMulticategoryFilter = new ElementMulticategoryFilter(new List<BuiltInCategory>
			{
				(BuiltInCategory)(-2000260),
				(BuiltInCategory)(-2000263)
			});
			ExtensibleStorageFilter extensibleStorageFilter = new ExtensibleStorageFilter(Utility.guidXData);
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(doc, doc.ActiveView.Id);
			ElementFilter elementFilter = new LogicalAndFilter(elementMulticategoryFilter, extensibleStorageFilter);
			return filteredElementCollector.WherePasses(elementFilter).WhereElementIsNotElementType().ToElementIds();
		}

		private static void WriteDataToDimension(Element elem)
		{
			if (elem == null)
			{
				return;
			}
			try
			{
				Schema schema = Schema.Lookup(Utility.guidXData);
				if (schema == null)
				{
					SchemaBuilder schemaBuilder = new SchemaBuilder(Utility.guidXData);
					schemaBuilder.SetSchemaName("HY_VerticalAutoDimension");
					schemaBuilder.AddSimpleField("Name", typeof(string));
					schema = schemaBuilder.Finish();
				}
				Entity entity = new Entity(schema);
				Field field = schema.GetField("Name");
				entity.Set<string>(field, "VerticalDimension");
				elem.SetEntity(entity);
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		public static void ModifyLevel(Autodesk.Revit.DB.Document doc, IList<IPlanarGeo> ArrayLevelGeo, XYZ ptLeft, XYZ ptRight)
		{
			if (doc == null || 0 >= ArrayLevelGeo.Count)
			{
				return;
			}
			Transaction transaction = new Transaction(doc);
			try
			{
				transaction.Start("modify Level");
				foreach (IPlanarGeo planarGeo in ArrayLevelGeo)
				{
					Level level = planarGeo.Element as Level;
					Utility.ModifyLevelParameter(doc, level);
					Utility.ModifyLevelPosition(doc, level, ptLeft, ptRight);
				}
				transaction.Commit();
			}
			catch (Exception ex)
			{
				if (1 == (int)transaction.GetStatus())
				{
					transaction.RollBack();
				}
				throw ex;
			}
		}

		public static void DoElevDim(Autodesk.Revit.DB.Document doc, List<IPlanarGeo> lstSpotGeo, SpotDimensionType spotDimType)
		{
			if (doc == null || lstSpotGeo.Count == 0 || spotDimType == null)
			{
				return;
			}
			Transaction transaction = new Transaction(doc);
			try
			{
				transaction.Start("create spot");
				foreach (IPlanarGeo planarGeo in lstSpotGeo)
				{
					XYZ point = (planarGeo.Max3DPointWCS + new XYZ(planarGeo.Min3DPointWCS.X, planarGeo.Min3DPointWCS.Y, planarGeo.Max3DPointWCS.Z)) / 2.0;
					Utility.WriteDataToDimension(Utility.CreateSpotDimension(doc, point, spotDimType));
					if (planarGeo.Weight == 10 || planarGeo.Weight == 9)
					{
						point = (planarGeo.Min3DPointWCS + new XYZ(planarGeo.Max3DPointWCS.X, planarGeo.Max3DPointWCS.Y, planarGeo.Min3DPointWCS.Z)) / 2.0;
						Utility.WriteDataToDimension(Utility.CreateSpotDimension(doc, point, spotDimType));
					}
				}
				transaction.Commit();
			}
			catch (Exception ex)
			{
				if (1 == (int)transaction.GetStatus())
				{
					transaction.RollBack();
				}
				throw ex;
			}
		}

		private static SpotDimension CreateSpotDimension(Autodesk.Revit.DB.Document doc, XYZ point, SpotDimensionType spotDimType)
		{
			if (doc == null || point == null || spotDimType == null)
			{
				return null;
			}
			SpotDimension result;
			try
			{
				Line line = Line.CreateBound(point, point + doc.ActiveView.RightDirection * 0.01);
				Reference reference = doc.Create.NewDetailCurve(doc.ActiveView, line).GeometryCurve.Reference;
				SpotDimension spotDimension = doc.Create.NewSpotElevation(doc.ActiveView, reference, point, point, point, point, false);
				spotDimension.DimensionType = spotDimType;
				result = spotDimension;
			}
			catch (Exception ex)
			{
				throw ex;
			}
			return result;
		}

		public static List<Dimension> DoDim(Autodesk.Revit.DB.Document doc, IList<GeoGroupNode> lstVerticalDimNode, DimensionType dimType)
		{
			if (doc == null || lstVerticalDimNode.Count == 0 || dimType == null)
			{
				return null;
			}
			Transaction transaction = new Transaction(doc);
			List<Dimension> result;
			try
			{
				transaction.Start("draw three dimension");
				List<Dimension> list = new List<Dimension>();
				foreach (GeoGroupNode geoGroupNode in lstVerticalDimNode)
				{
					if (geoGroupNode.ArrayGeo != null)
					{
						List<double> list2 = new List<double>();
						foreach (IPlanarGeo planarGeo in geoGroupNode.ArrayGeo)
						{
							list2.Add(planarGeo.TopElev);
							if (planarGeo.Weight == 10 || planarGeo.Weight == 9)
							{
								list2.Add(planarGeo.BottomElev);
							}
						}
						list2.Sort();
						for (int i = 0; i < list2.Count; i++)
						{
							for (int j = i + 1; j < list2.Count; j++)
							{
								if (MathUtility.IsEqual(list2[i], list2[j], 1E-06))
								{
									list2.RemoveAt(j);
									j--;
								}
							}
						}
						Dimension dimension = Utility.CreateDimByElevator(doc, list2, dimType, geoGroupNode.Point, geoGroupNode.Position);
						Utility.WriteDataToDimension(dimension);
						list.Add(dimension);
					}
				}
				transaction.Commit();
				result = list;
			}
			catch (Exception ex)
			{
				if (1 == (int)transaction.GetStatus())
				{
					transaction.RollBack();
				}
				throw ex;
			}
			return result;
		}

		public static List<Dimension> DoDimWinDoor(Autodesk.Revit.DB.Document doc, IList<GeoVerticalDimResult> lstWinDoorDim, DimensionType dimType, Transform transformWcs2Ecs)
		{
			if (lstWinDoorDim.Count == 0 || dimType == null || transformWcs2Ecs == null)
			{
				return null;
			}
			Transaction transaction = new Transaction(doc);
			List<Dimension> result;
			try
			{
				transaction.Start("win and door dimension");
				List<Dimension> list = new List<Dimension>();
				foreach (GeoVerticalDimResult geoVerticalDimResult in lstWinDoorDim)
				{
					if (0 < geoVerticalDimResult.ArrayGeo.Count)
					{
						List<IPlanarGeo> list2 = new List<IPlanarGeo>();
						List<IPlanarGeo> list3 = new List<IPlanarGeo>();
						foreach (IPlanarGeo planarGeo in geoVerticalDimResult.ArrayGeo)
						{
							if (planarGeo.Weight == 11)
							{
								list3.Add(planarGeo);
							}
							else
							{
								list2.Add(planarGeo);
							}
						}
						IPlanarGeo planarGeo2 = null;
						List<double> list4 = new List<double>();
						foreach (IPlanarGeo planarGeo3 in list2)
						{
							list4.Add(planarGeo3.TopElev);
							list4.Add(planarGeo3.BottomElev);
							for (int i = 0; i < list3.Count; i++)
							{
								IPlanarGeo planarGeo4 = list3[i];
								if (MathUtility.GreaterThan(planarGeo3.TopElev, planarGeo4.BottomElev, 1E-06) && MathUtility.LessThan(planarGeo3.BottomElev, planarGeo4.BottomElev, 1E-06))
								{
									list3.RemoveAt(i);
									i--;
								}
							}
							if (planarGeo2 == null)
							{
								planarGeo2 = planarGeo3;
							}
							else if (MathUtility.LessThan(planarGeo2.Max2DPointECS.X, planarGeo3.Max2DPointECS.X, 1E-06))
							{
								planarGeo2 = planarGeo3;
							}
						}
						foreach (IPlanarGeo planarGeo5 in list3)
						{
							list4.Add(planarGeo5.BottomElev);
						}
						list4.Sort();
						for (int j = 0; j < list4.Count; j++)
						{
							for (int k = j + 1; k < list4.Count; k++)
							{
								if (MathUtility.IsEqual(list4[j], list4[k], 1E-06))
								{
									list4.RemoveAt(k);
									k--;
								}
							}
						}
						XYZ xyz = doc.ActiveView.RightDirection.Normalize();
						XYZ point = planarGeo2.Max3DPointWCS + xyz * UnitUtility.MMToFeet(600.0);
						Dimension dimension = Utility.CreateDimByElevator(doc, list4, dimType, point, false);
						Utility.WriteDataToDimension(dimension);
						list.Add(dimension);
					}
				}
				transaction.Commit();
				result = list;
			}
			catch (Exception ex)
			{
				if (1 == (int)transaction.GetStatus())
				{
					transaction.RollBack();
				}
				throw ex;
			}
			return result;
		}

		public static Dimension DoDimRailing(Autodesk.Revit.DB.Document doc, IPlanarGeo geoRailing, IList<IPlanarGeo> lstLevelGeo, DimensionType dimType)
		{
			if (doc == null || geoRailing == null || lstLevelGeo.Count == 0 || dimType == null)
			{
				return null;
			}
			IPlanarGeo planarGeo = null;
			for (int i = lstLevelGeo.Count - 1; i >= 0; i--)
			{
				IPlanarGeo planarGeo2 = lstLevelGeo[i];
				if (MathUtility.GreaterThan(geoRailing.TopElev, planarGeo2.BottomElev, 1E-06))
				{
					planarGeo = planarGeo2;
					break;
				}
			}
			Transaction transaction = new Transaction(doc);
			Dimension result;
			try
			{
				transaction.Start("create railing dimension");
				Line line = Line.CreateBound((new XYZ(geoRailing.Min3DPointWCS.X, geoRailing.Min3DPointWCS.Y, geoRailing.Max3DPointWCS.Z) + geoRailing.Max3DPointWCS) / 2.0, doc.ActiveView.RightDirection * 0.01);
				ReferenceArray referenceArray = new ReferenceArray();
				Reference reference = new Reference(doc.Create.NewDetailCurve(doc.ActiveView, line));
				referenceArray.Append(reference);
				referenceArray.Append(new Reference(planarGeo.Element as Level));
				Dimension dimension = doc.Create.NewDimension(doc.ActiveView, line, referenceArray, dimType);
				Utility.WriteDataToDimension(dimension);
				transaction.Commit();
				result = dimension;
			}
			catch (Exception ex)
			{
				throw ex;
			}
			return result;
		}

		private static Dimension CreateDimByElevator(Autodesk.Revit.DB.Document doc, List<double> lstAllElevators, DimensionType dimType, XYZ point, bool bLeft = true)
		{
			if (doc == null || lstAllElevators.Count == 0 || dimType == null || point == null)
			{
				return null;
			}
			Dimension result;
			try
			{
				Line line = null;
				if (bLeft)
				{
					line = Line.CreateBound(new XYZ(point.X, point.Y, lstAllElevators[lstAllElevators.Count - 1]), new XYZ(point.X, point.Y, lstAllElevators[0]));
				}
				else
				{
					line = Line.CreateBound(new XYZ(point.X, point.Y, lstAllElevators[0]), new XYZ(point.X, point.Y, lstAllElevators[lstAllElevators.Count - 1]));
				}
				ReferenceArray referenceArray = new ReferenceArray();
				foreach (double num in lstAllElevators)
				{
					XYZ xyz = new XYZ(point.X, point.Y, num);
					XYZ xyz2;
					if (bLeft)
					{
						xyz2 = xyz + doc.ActiveView.RightDirection * 0.01;
					}
					else
					{
						xyz2 = xyz - doc.ActiveView.RightDirection * 0.01;
					}
					Line line2 = Line.CreateBound(xyz, xyz2);
					Reference reference = new Reference(doc.Create.NewDetailCurve(doc.ActiveView, line2));
					referenceArray.Append(reference);
				}
				result = doc.Create.NewDimension(doc.ActiveView, line, referenceArray, dimType);
			}
			catch (Exception ex)
			{
				throw ex;
			}
			return result;
		}

		private static void ModifyLevelParameter(Autodesk.Revit.DB.Document doc, Level level)
		{
			if (doc == null || level == null)
			{
				return;
			}
			try
			{
				LevelType levelType = doc.GetElement(level.GetTypeId()) as LevelType;
				Parameter parameter = levelType.get_Parameter(BuiltInParameter.DATUM_BUBBLE_END_1);
				if (parameter != null)
				{
					parameter.Set(1);
				}
				parameter = levelType.get_Parameter(BuiltInParameter.DATUM_BUBBLE_END_2);
				if (parameter != null)
				{
					parameter.Set(1);
				}
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		private static void ModifyLevelPosition(Autodesk.Revit.DB.Document doc, Level level, XYZ ptLeftPos, XYZ ptRightPos)
		{
			if (doc == null || level == null || ptLeftPos == null || ptRightPos == null)
			{
				return;
			}
			try
			{
				Autodesk.Revit.DB.View activeView = doc.ActiveView;
				if (level.CanBeVisibleInView(activeView))
				{
                    IList<Curve> curvesInView = level.GetCurvesInView((DatumExtentType)1, activeView);
					if (0 < curvesInView.Count)
					{
						Curve curve = curvesInView[0];
						XYZ endPoint = curve.GetEndPoint(0);
						XYZ endPoint2 = curve.GetEndPoint(1);
						if (endPoint != null && endPoint2 != null)
						{
							Utility.ModifyEndPoint(activeView, ptLeftPos, ref endPoint);
							Utility.ModifyEndPoint(activeView, ptRightPos, ref endPoint2);
							Curve curve2 = Line.CreateBound(endPoint, endPoint2);
                            level.SetCurveInView((DatumExtentType)1, doc.ActiveView, curve2);
						}
					}
				}
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		private static void ModifyEndPoint(Autodesk.Revit.DB.View view, XYZ ptPosition, ref XYZ ptLevel)
		{
			if (view == null || ptPosition == null || ptLevel == null)
			{
				return;
			}
			try
			{
				Line line = Line.CreateUnbound(XYZ.Zero, view.RightDirection);
				IntersectionResult intersectionResult = line.Project(ptPosition);
				IntersectionResult intersectionResult2 = line.Project(ptLevel);
				if (intersectionResult != null && intersectionResult2 != null)
				{
					double parameter = intersectionResult.Parameter;
					double parameter2 = intersectionResult2.Parameter;
					if (MathUtility.GreaterThan(parameter - parameter2, 0.0))
					{
						ptLevel += view.RightDirection * (parameter - parameter2);
					}
					else
					{
						ptLevel -= view.RightDirection * (parameter2 - parameter);
					}
				}
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		public static readonly Guid guidXData = new Guid("D9EC7005-A43A-4183-BB65-870F10AF4011");
	}
}
